javascript

[ES6] 13. Map, WeakMap

7 min read|17. 6. 5.

[ES6] 13. Map, WeakMap API

Map Object

아시다시피, Map오브젝트는 key-value의 자료구조입니다. 기존 자바스크립트의 Object도 마찬가지로 key-value형식으로 구성되는데요, Map오브젝트는 key로 사용될 수 있는 타입이 다양하다는 것이 다른 점이라고 할 수 있습니다. 또한 Map오브젝트는 Object 오브젝트와는 달리 이터러블 오브젝트입니다. 그렇기 때문에 for-of statement를 사용하여 순회할 수 있습니다.

Map오브젝트는 새로 추가되는 값의 key와 기존에 저장되어 있던 데이터의 key의 값이 동일하면 추가되지 않습니다. Java에서의 Map은 key를 hash값으로 저장하기 때문에 동일한 hash 값에 대해서 추가하지 않는데요, JavaScript에서는 조금 다른 방식으로 이를 판단합니다. key와 value가 저장될 때, 엔진이 별도의 일련 번호를 부여하여 이를 판단하게 됩니다. 또한 이 일련번호로 추가된 순서를 보장해줍니다.

다음과 같이 정리할 수 있겠습니다.
  • 다양한 타입으로 key를 정의할 수 있다.
  • 이터러블 오브젝트(iterable object)이다.
  • 중복된 key를 추가할 수 없다.
  • 중복된 key를 통해 추가하는 경우 해당 keyvalue를 덮어쓴다.
    • 즉 기존 key의 순서를 유지한다.
  • 순서를 보장하는 자료구조이다.

Notice

예제 코드의 길이을 줄이기 위해 Map의 key와 value를 확인하기 위한 함수를 정의해두고 사용하겠습니다 :)

function printMap(map) {
  for (let [key, value] of map) {
    console.log(`key: ${key}, value: ${value}`)
  }
}
// Expected console
// key: something, value: something

Map은 이터러블 오브젝트이기 때문에 [key, value]형식으로 값을 받아서 사용할 수 있습니다. 물론 다음과 같이 함수를 정의할 수도 있습니다.

function print(map) {
  for (let item of map) {
    console.log(Array.isArray(item)) //true
    console.log(item)
  }
}
//Expected console
// [ 'something', 'something' ]

for-of statement에서 추출된 itemArray입니다. 따라서 console에도 Array 타입으로 찍힙니다.

new Map()

Map 오브젝트는 다음과 같이 생성가능합니다.

let newMap = new Map([['1', 'one'], ['2', 'two'], ['3', 'three']])

printMap(newMap)
// console
// key: 1, value: one
// key: 2, value: two
// key: 3, value: three

Map.prototype.set(), get()

key와 value를 설정합니다. set() 메소드의 반환 값은 set() 메소드를 호출한 Map입니다. 그래서 chaining처럼 연속적으로 key와 value를 설정해줄 수 있습니다. get()메소드는 인자로 넘겨간 key값과 일치하는 value를 반환합니다.

let webs = new Map()
webs
  .set('1', 'React')
  .set('2', 'Angular2')
  .set('3', 'Vue')
  .set('2', 'Redux')

printMap(webs)
console.log(`webs.get('1'): ${webs.get('1')}`)
// console
// key: 1, value: React
// key: 2, value: Redux
// key: 3, value: Vue
// webs.get('1'): React

Map.prototype.entries(), keys(), values()

각각 해당하는 이터레이터 오브젝트를 반환하는 메소드입니다.

Map.prototype.has()

.has()는 파라미터로 전달되는 key에 해당하는 값이 존재하는지에 대한 여부를 boolean으로 반환합니다.

console.log(webs.has('1')) //true
console.log(webs.has('4')) //false

Map.prototype.delete(), clear()

.delete()메소드의 파라미터로 특정 key값을 넘겨주면 해당 key와 value가 map에서 삭제됩니다. .clear()는 모든 keyvalue를 삭제합니다.

console.log(webs.size) // 3
webs.delete('1')
console.log(webs.size) // 2
printMap(webs)
// console
// key: 2, value: Redux
// key: 3, value: Vue

webs.clear()
console.log(webs.size) // 0
printMap(webs)
// console
// undefined

Map.prototype.forEach

forEach의 파라미터로는 콜백 함수를 넘겨줍니다.

webs.forEach((value, key, obj) => {
  console.log(`key: ${key}, value: ${value}`)
})

// console
// key: 1, value: React
// key: 2, value: Redux
// key: 3, value: Vue

넘겨지는 콜백 함수에는 세 가지 인자가 전달될 수 있습니다. key, value의 순서가 조금 다르다는 것에 주의하면 되겠네요. 세 번째 인자로는 콜백 함수 내부에서 this로 참조할 오브젝트가 전달됩니다.

WeakMap Object

WeakMap 오브젝트의 key에는 Object만 지정될 수 있습니다.

GC와의 연관성

key로 사용하고 있던 Object 오브젝트가 메모리에서 사라질 경우, 즉 GC에 의해서 Garbage Colleting이 되면, 더이상 valuekey로서 역할을 수행하지 못합니다. 그렇기 때문에 이 key에 대한 내용을 삭제해줘야 한다. 메모리에서 사라진 key에 대해서 삭제하는 작업을 WeakMap을 사용하면 자동으로 해결됩니다.

API

set(), get(), has(), delete() 이 네 가지 API만 제공할 뿐, 열거를 위한 API는 제공하지 않습니다. 또한 size 프로퍼티가 존재하지 않기 때문에, 특정 시점의 [key, value]의 수를 알 수 없습니다. 이는 WeakMap 자체적으로 key를 제거하므로 값이 비결정적이기 때문에 API를 통해 제공되지 않습니다.

cf) Set 자료구조는 Map자료구조의 keyvalue가 들어가게 되는 자료구조 입니다. 그러므로 Map과 거의 유사한 API를 제공하며 저장하는 방식의 차이만 존재합니다. 마찬가지로 SetWeakSetMapWeakMap의 관계와 동일합니다.

예제로 사용된 코드는 Github Respository에서 확인하실 수 있습니다.

13. end